#include <iostream>
#include <pthread.h>
#include <cstdio>
#include <vector>
#include <sys/types.h>
#include <unistd.h>
#include <cstring>

//目标:1.创建一个线程     2.传入的参数要求有类对象和普通对象 3.使用线程等待,并输出返回值(void*强转这方面) 
//     4.使用一次进程取消 5.使用一次线程分离                 6.使用两种不同方法在不影响其他线程的情况下退出线程
using namespace std;


struct threadname
{
    threadname(int num)//用自己设定的编号来描述每个线程
    {
        _name += "thread->" + to_string(num);
    }
    string _name;
};

string anHex(int tid)
{
    char buffer[32];
    snprintf(buffer, sizeof(buffer), "%x", tid);//转化为16进制
    return string(buffer);//传一个匿名对象,方便理解
};

void* threadhandler(void* arg)
{
    //希望线程函数实现以下功能：
    //1.打印自己的进程pid 2.打印自己的线程编号 3.打印自己的tid 需要自定义一个类型，用来将返回的tid变为16进制的值(这里的类型名为anHex)
    threadname* name = static_cast<threadname*> (arg);
    printf("用户设置的线程编号: %s, 线程(进程)pid: %d, 线程本身的tid: 0x%s\n",name->_name.c_str(), getpid(), anHex(pthread_self()).c_str());//pthread_self()是返回当前线程的tid
    // int cnt = 5;
    // while(cnt--)
    // {
    //     sleep(1);
    //     printf("新线程: cnt = %d, &cnt = %p\n", cnt, &cnt);
    // }



    //两种不影响其他线程,退出本线程的方式

    //return (void*)1;//返回一个1
    //使用另一种方式退出线程
    sleep(2);
    pthread_detach(pthread_self());//线程分离
    sleep(2);
    pthread_cancel(pthread_self());//一般不会自己取消自己，既没有返回值又不好控制取消时机，不如直接用分离，还能自动释放资源
    cout << "什么,线程没被取消？" << endl;
    sleep(10);
    pthread_exit((void*)100);
}

int main()
{
    //创建类对象
    threadname name(0);//由于只创建一个线程,因此编号默认为1
    vector<pthread_t> tids;
    //创建线程
    pthread_t tid;
    pthread_create(&tid, nullptr, threadhandler, (void*)&name);
    tids.push_back(tid);//将获得的线程的tid返回给tids(tid是用户层面的)

    //printf("主线程第一次观察: cnt = %d, &cnt = %p", cnt, &cnt); 这里报错是因为,线程的栈结构是独立的，不过也不是无法查看,毕竟独立栈不是私有而是共有的
    //进程等待
    // pthread_cancel(pthread_self());
    sleep(3);
    int test = pthread_cancel(tids[0]);
    // int test = pthread_detach(tids[0]);
    if(test != 0){cout << "当进程等待新线程后,对一个已经分离的线程进行取消,得到的值为: " << test << " : "<<strerror(test) <<endl;}
    else if(test == 0) {cout << "成功分离子线程" << endl;}

    void* retval;
    int n = pthread_join(tids[0], &retval);//&retval类似于二级指针，输入输出型参数，想要修改retval的内容只能用&retval，而retval又是一个指针，因此采用二级指针的方式
    if(n == 0 && (long long int)retval != -1)
        cout << "新线程返回的结果是: " << (long long int)retval << endl; //将retval强转为我们需要的类型
    // else if(n == -1)
    //     cout << "线程被取消,返回: " << n << endl;
    else if((long long int)retval == -1)
        cout << "等待的线程被取消,返回的输入输出型参数为-1 " << endl;
    else//n是一个错误码
        cout << "线程被分离,错误消息为: " << strerror(n) << endl;
    //关于强转void*,我们可以将其强转为类或者内置类型,事实上类就是多个内置类型的组合
    //CPU只要获得一个起始地址,而我们的类型就是偏移量,CPU会用起始地址＋偏移量的方式,去进行内存划分

    sleep(3);
    n = pthread_join(tids[0], &retval);
    cout << "当新线程被分离后,又进行返回,此时主线程进行进程等待到的值为: " << n << endl;
    return 0;
}